package org.weso.acota.dao.feedback;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.weso.acota.to.feedback.FeedbackResource;
import org.weso.acota.to.feedback.FeedbackTag;
import org.weso.acota.to.feedback.FeedbackUser;
import org.weso.acota.to.feedback.TagType;

import virtuoso.jena.driver.VirtGraph;
import virtuoso.jena.driver.VirtuosoUpdateFactory;
import virtuoso.jena.driver.VirtuosoUpdateRequest;

import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Resource;

public class FeedbackVirtuosoDAO implements FeedbackDAO {
	
	protected static Logger logger = Logger.getLogger(FeedbackVirtuosoDAO.class);
	private String base_uri;
	private Properties properties;
	
	private String data_url; 
	private String endpoint_url;
	private String virtuoso_jdbc;
	

	public FeedbackVirtuosoDAO(){
		this.properties = new Properties();
		try {
			InputStream propertiesStrem = Thread.currentThread().getContextClassLoader().getResourceAsStream("resources/config.properties");
			properties.load(propertiesStrem);
		} catch (IOException ex) {
		    ex.printStackTrace();
		}
		
		//Configuration in properties file
		this.data_url = this.properties.getProperty("data_url");
		this.endpoint_url = this.properties.getProperty("endpoint_url");
		this.base_uri = this.properties.getProperty("base_uri");
		this.virtuoso_jdbc = this.properties.getProperty("virtuoso_jdbc");
	}
	
	private String getDocUri(String activity){
		return base_uri + 
				activity + "/" + 
				(new Date()).getTime() + "/";
	}
	
	private TagType getTagType(Resource tag){
		if(tag.getLocalName().equals("ReaderTag")){
			return TagType.SELECTED;
		}else if(tag.getLocalName().equals("AutoTag")){
			return TagType.PROVIDED;
		}
		// TODO Otro tipo de retorno si se consideran tags negativamente
		return TagType.PROVIDED;
	}
	
	@Override
	public List<FeedbackTag> getDocumentTags(String documentUri) {
		VirtGraph  set= new VirtGraph (virtuoso_jdbc, this.properties.getProperty("virtuoso_user"), this.properties.getProperty("virtuoso_pass"));		
		String query = getPrefix();
		query += "SELECT ?type ?label ?weight\n";
		query += "FROM <"+data_url+">\n";
		query += "WHERE{\n";
		query += "?document dc:title ?title.\n";
		query += "?document ctag:tagged ?tag.\n";
		query += "?tag rdf:resource ?tagEntity.\n";
		query += "?tag acota:weight ?weight.\n";
		query += "?tagEntity ctag:label ?label.\n";
		query += "?tag rdf:type ?type\n";
		query += "FILTER(?document=<"+documentUri+">)\n";
		query += "}\n";
		
		try{
			QueryExecution qe = QueryExecutionFactory.sparqlService(endpoint_url, query);
			
			List<FeedbackTag> tags = new LinkedList<FeedbackTag>();
			
			ResultSet result = qe.execSelect();
			while(result.hasNext()) {
			        QuerySolution sol = result.next();
			        String label = sol.get("label").toString();
			        int weight = Integer.parseInt(sol.get("weight").toString());
			        Resource type = sol.get("type").asResource();		        
			        tags.add(new FeedbackTag(label, weight, getTagType(type)));
			}
			logger.info("Query tags by documents ("+tags.size()+")");
			qe.close();
			set.close();
			return tags;
		}catch (Exception e) {
			set.close();
			logger.info("ERROR \n(Query tags by documents):\n"+e.toString());
			return null;
		}
	}
	
	@Override
	public List<FeedbackResource> getTaggedDocuments(String label) {
		String query =  getPrefix();
		query += "SELECT ?label ?title ?description ?source ?subject ?means \n";
		query += "FROM <"+data_url+">\n";
		query += "WHERE{\n";
		query += "?tag ctag:label ?label.\n";
		query += "?tag ctag:means ?means.\n";
		query += "?means dc:title ?title.\n";
		query += "?means dc:description ?description.\n";
		query += "?means dc:source ?source.\n";
		query += "?means dc:subject ?subject.\n";
		query += "FILTER(?label =\""+label+"\")\n";
		query += "}order by ?title\n";
		
		try{
			QueryExecution qe = QueryExecutionFactory.sparqlService(endpoint_url, query);
			
			List<FeedbackResource> resources = new LinkedList<FeedbackResource>();
			
			ResultSet result = qe.execSelect();
			while(result.hasNext()) {
			        QuerySolution sol = result.next();
			        String title = sol.get("title").asLiteral().getString();
			        String description = sol.get("description").asLiteral().getString();
			        String source = sol.get("source").asLiteral().getString();
			        String subject = sol.get("subject").asLiteral().getString();
			        String docUri = sol.get("means").toString();
			        resources.add(new FeedbackResource(title, description, source, subject, getDocumentTags(docUri)));
			}
			
			logger.info("Query documents by tags ("+resources.size()+")");
			
			qe.close();
			return resources;	
		}catch (Exception e) {
			logger.info("ERROR:"+e.toString());
			return null;
		}
	}
	
	@Override
	public List<FeedbackTag> getUserTags(String username) {
			
		String query = getPrefix();
		query += "SELECT ?weight ?label ?type ?name ?document ?tag ?tagEntity \n";
		query += "FROM <"+data_url+">\n";
		query += "WHERE{\n";
		query += "?document rdf:type foaf:Document .\n";
		query += "?document ctag:tagged ?tag .\n";
		query += "?tag rdf:type ?type.\n";
		query += "?tag acota:weight ?weight.\n";
		query += "?tag rdf:resource ?tagEntity.\n";
		query += "?tagEntity ctag:label ?label.\n";
		query += "?tag ctag:AuthorTag ?author.\n";
		query += "?author foaf:name ?name\n";
		query += "Filter(?name=\""+username+"\")\n";
		query += "}order by ?label";
		
		try{
			QueryExecution qe = QueryExecutionFactory.sparqlService(endpoint_url, query);
			
			List<FeedbackTag> tags = new LinkedList<FeedbackTag>();
			
			ResultSet result = qe.execSelect();
			while(result.hasNext()) {
			        QuerySolution sol = result.next();
			        String label = sol.get("label").toString();
			        int weight = Integer.parseInt(sol.get("weight").toString());
			        Resource type = sol.get("type").asResource();
			        
			        tags.add(new FeedbackTag(label, weight, getTagType(type)));
			}
			
			logger.info("Query tags by users ("+tags.size()+")");
			
			qe.close();
			return tags;	
		}catch (Exception e) {
			logger.info("ERROR:"+e.toString());
			return null;
		}
		
	}
	
	public String getPrefix(){
		String prefix = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n";
		prefix += "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n";
		prefix += "PREFIX dc: <http://weso.es/dc/elements/1.1/>\n";
		prefix += "PREFIX ctag: <http://commontag.org/ns#>\n";
		prefix += "PREFIX acota: <http://weso.es/weso/acota#>\n";
		return prefix;
	}	
	
	@Override
	public String saveTag(FeedbackTag tag) {
		VirtGraph  set= new VirtGraph (virtuoso_jdbc, this.properties.getProperty("virtuoso_user"), this.properties.getProperty("virtuoso_pass"));
		String query = getPrefix();
		
		//Set base uri for tag entities
		String baseUri = "<"+data_url+"/tag/"+encode(tag.getLabel().toLowerCase().trim().replace(" ", "_"))+"/>";
		query+="INSERT INTO GRAPH <"+data_url+">\n{";
		query+= baseUri+" rdf:type ctag:Tag .\n";
		query+= baseUri+" ctag:label \""+encode(tag.getLabel().toString().toLowerCase())+"\".\n";
		query+="}";
		
		try{
			VirtuosoUpdateRequest vur = VirtuosoUpdateFactory.create(query, set);
			vur.exec();
			//set.close();
			logger.info("Insert new tag");
			set.close();
			return baseUri;
		}catch (Exception e) {
			System.out.println(e.toString()+"\n"+query);
			set.close();
			return "";
		}
	}

	@Override
	public String saveUser(FeedbackUser user) {
		VirtGraph  set= new VirtGraph (virtuoso_jdbc, this.properties.getProperty("virtuoso_user"), this.properties.getProperty("virtuoso_pass"));
		
		String query = getPrefix();
		
		//Set base uri for tag entities
		String baseUri = "<"+data_url+"/user/"+encode(user.getId())+"/>";
		query+="INSERT INTO GRAPH <"+data_url+">\n{";
		query += baseUri + " rdf:type foaf:Person .\n";
		query += baseUri + " foaf:nick \"" + encode(user.getId()) + "\" .\n";
		query += baseUri + " foaf:name \"" + encode(user.getName()) + "\" .\n";
		query+="}";
		
		try{
			VirtuosoUpdateRequest vur = VirtuosoUpdateFactory.create(query, set);
			vur.exec();
			//set.close();
			logger.info("Insert new user");
			set.close();
			return baseUri;
		}catch (Exception e) {
			System.out.println(e.toString()+"\n"+query);
			set.close();
			return "";
		}
	}
	
	public String saveSuggestions(String user, String activity, Date taggingDate,
			String resourceUri, String resourceLabel,
			String resourceDescription, List<FeedbackTag> tags) {		
		
		//Save user tagger
		FeedbackUser userSaved = new FeedbackUser();
		userSaved.setId(user);
		userSaved.setName(user);
		String userRdf = saveUser(userSaved);
		
		String baseDocUri = getDocUri(activity);
		String docUri = "<" + baseDocUri + ">";
		
		String query = getPrefix();
		query += "INSERT INTO GRAPH <" + data_url + ">\n";
		query += "{\n";
		//Add Document info
		query += docUri + " rdf:type foaf:Document .\n";
		query += docUri + " dc:title \"" + encode(resourceLabel) + "\" .\n";
		query += docUri + " dc:description \"" + encode(resourceDescription) + "\" .\n";
		query += docUri + " dc:source \"" + encode(resourceUri) + "\" .\n";
		query += docUri + " dc:subject \"" + encode(activity) + "\" .\n";
		
		//Add user-document relation
		query += userRdf + " foaf:interest "+ docUri +" . \n";
		
		//The relation document list of tags have its own uri
		String docTagUri = "<" + getDocUri(activity) +"tag/"+ "";
		int tagUriCont =0;
		for(int i=0; i< tags.size(); i++){
			//Save each tag
			String tagUri = saveTag(tags.get(i));	
			
			if(tags.get(i).getTagType().toString().equals("SELECTED")){
				//Add document-tag relation
				query += docTagUri +tagUriCont+"/>"+ " rdf:type ctag:ReaderTag .\n";
				query += docTagUri +tagUriCont+"/>"+ " rdf:resource " + tagUri + " .\n";
				query += docTagUri +tagUriCont+"/>"+ " acota:weight \"" + tags.get(i).getWeight() + "\" .\n";
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				query += docTagUri +tagUriCont+"/>"+ " ctag:taggingDate \"" + sdf.format(taggingDate) + "\" .\n";
				query += docTagUri +tagUriCont+"/>"+ " ctag:AuthorTag " + userRdf + " .\n";
				query += docTagUri +tagUriCont+"/>"+ " ctag:means \"" + docUri + "\" .\n";
				
				//Add tag-document relation
				query += tagUri + " ctag:means " + docUri + " .\n";
				//Add document-tag relation
				query += docUri + " ctag:tagged " + docTagUri +tagUriCont+"/> .\n";
				tagUriCont++;
			}
		}
		query += "}";
		VirtGraph  set= new VirtGraph (virtuoso_jdbc, this.properties.getProperty("virtuoso_user"), this.properties.getProperty("virtuoso_pass"));
		try{
			VirtuosoUpdateRequest vur = VirtuosoUpdateFactory.create(query, set);
			vur.exec();
			//set.close();
			logger.info("Insert new suggestion and document");
			set.close();
			return baseDocUri;
		}catch (Exception e) {
			System.out.println(e.toString()+"\n"+query);
			set.close();
			return "";
		}
	}
	
	public String encode(String text){
		String newText ="";
		newText=text.replace("\"", "\\\"");
		return newText;
	}
}
